/*
----------------------------------------------------------------------------------------------------
This source file is part of the Senbotsu Project
Copyright (c) 2007 by Ryan Holcombe

This program is free software; you can redistribute it and/or modify it under the terms of the GNU
Lesser General Public License as published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.
----------------------------------------------------------------------------------------------------
*/

#ifndef _goAnimated_h
#define _goAnimated_h

#include "goEntity.h"

class IState;
typedef std::vector<std::string> StateList;
typedef std::map<unsigned short, float> BoneMaskMap;
typedef std::map<std::string, IState> StateMap;
typedef std::map<std::string, StateList> AnimationMap;


/**
	Defines an entity which has animations. Custom animations can be defined through an xml interace

	\remarks
		Available XML Parameters\n
		StateCopyFrom - The new animation state will be based on an existing state\n
		BoneMaskDefault - set all bone weights to this initial value (default: 1)\n
		BoneInheritChildren - each BoneMask will parse it's child bones and set them to the same value (default: 0)\n
		BoneMask - bone name to mask along with its value\n\n
		Sample:\n
		<AnimationState name="AttackSwing1">\n
			<StateCopyFrom value="Attack1" type="string" />\n
			<BoneMaskDefault value="0" type="float" />\n
			<BoneInheritChildren value="1" type="int" />\n
			<BoneMask name="Bip01_Spine" value=".8" type="float" />\n
		</AnimationState>
*/

class goAnimated : public goEntity
{
	protected:

		StateMap		mStateMap;		//!< all the animation states for the entity
		AnimationMap	mAnimationMap;	//!< animations (combinations of states)
		std::string		mCurrAnim;		//!< current animation

	public:

		/**
			\brief loads the animations
			\param[in] file mesh file name, skeleton, if found, will be loaded as well
			\param[in] pSceneMgr pointer to the ogre scene manager
		*/
		virtual void load(const std::string& file, Ogre::SceneManager *pSceneMgr);


		/**
			\brief sets the animation, no blending is performed
			\param[in] newAnim the new animation to set
			\param[in] loop whether or not to loop the animation (default: true)
		*/
		virtual void setAnimation(std::string animation, bool loop = true);


		/**
			\brief creates all animation states present in a xml file
			\param[in] file xml file to load
		*/
		void createAllStatesFromXML(const std::string& file);


		/**
			\brief adds animations from the file
			\param[in] file xml file to load
			\remarks
				This will create animations which can be a combination of multiple
				animation states
		*/
		void addAnimationsFromXML(const std::string& file);


		/**
			\brief adds an animation
			\param[in] name reference name for the animation
			\param[in] list state names which will make up this animation
		*/
		void addAnimation(const std::string& name, const StateList& list);


		/**
			\brief outputs a list of bone names/positions to an iostream
			\param[in] out output the results in ascii format
		*/
		void _outputBoneList(std::ostream& out) const;


		/**
			\brief outputs a list of animations to an iostream
			\param[in] out output the results in ascii format
		*/
		void _outputAnimationList(std::ostream& out) const;

	private:

		/**
			\brief creates a state from a location in a xml file
			\param[in] xml file pointer to a Gamut::XMLParser object
		*/
		void _createState(Gamut::XMLParser *xml);


		/**
 			\brief creates all animations from the currently loaded states
			\remarks
				additional animations which require combining 2 or more states have to be loaded
				seperately using addAnimation or addAnimationsFromXML
		*/
		void _createAnimationsFromStates();


		/**
 			\brief enables an animation state
			\param[in] state name of the state to enable
			\param[in] loop true to loop, false to run once
		*/
		void _enableState(const std::string& stateName, bool loop);


		/**
 			\brief enables an animation state
			\param[in] state name of the state to disable
		*/
		void _disableState(const std::string& state);

};



/**
	Defines an animation state. All standard states from the entity are referenced as well as any states that require blending
*/

class IState
{
	friend class goAnimated;

	private:
		Ogre::AnimationState *mBaseState;		//!< default OGRE state from which this state is derived
		float mDefaultBoneWeight;				//!< weight of all bones not referenced in the BoneMaskMap
		BoneMaskMap mBoneMaskMap;				//!< bones which require a custom weight

	public:

		/** Default Constructor
		*/
		IState() : mBaseState(0), mDefaultBoneWeight(1.0f) { }


		/**
			\brief constructor which sets the default bone weight
		*/
		IState(float defaultWeight) : mBaseState(0), mDefaultBoneWeight(defaultWeight) { }


		/**
			\brief adds an entry to the BoneMaskMap
			\param[in] bone adds this bone's handle
			\param[in] weight the weight (0.0 to 1.0) to set the bone to
			\param[in] recurseChildren if true, add all child bones as well
		*/
		void setBoneMask(Ogre::Bone *bone, float weight, bool recurseChildren = false)
		{
			mBoneMaskMap[bone->getHandle()] = weight;
			if (recurseChildren)
			{
				Ogre::Node::ChildNodeIterator iter = bone->getChildIterator();
				while (iter.hasMoreElements())
					setBoneMask(dynamic_cast<Ogre::Bone*>(iter.getNext()), weight, true);
			}

		}
};


#endif
